﻿using System;

namespace CurrencyCombinations
{
    class Program
    {
        #region [ Entry Point ]
        static void Main(string[] args)
        {
            while (true)
            {
                #region [ Main Menu ]
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Currency Combinations");

                Console.WriteLine("Please select the option number inside the [] you prefer:");
                Console.WriteLine("[1] Currency combination");
                Console.WriteLine("[2] Currency combination under Anarchy:");
                Console.WriteLine("[3] Exit");
                Console.Write("Option: ");

                string option = Console.ReadLine();

                while (option != "1" && option != "2" && option!= "3")
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.Write("Option not valid. Please select 1 or 2 or 3:");
                    Console.ResetColor();

                    option = Console.ReadLine();
                }

                Console.WriteLine();
                Console.ResetColor();
                #endregion

                switch (option)
                {
                    #region [ Option 1]
                    case "1":
                        Console.WriteLine("Please provide the total you want to change for quartes, dimes, nickels and pennies: ");
                        Console.Write("Total: ");
                        string input = Console.ReadLine();
                        Console.WriteLine();

                        int result = 0;

                        if (int.TryParse(input, out result))
                        {
                            try
                            {
                                CurrencyUnderDemocracy(result);
                            }
                            catch (ArgumentException ae)
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine(ae.Message);
                            }
                        }
                        else
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("The input value is invalid");
                            Console.ResetColor();
                        }
                        break;
                    #endregion
                    #region [ Option 2 ]
                    case "2":
                        Console.WriteLine("Oh my... You are living on anarchy!");
                        Console.WriteLine("Ok, please provide the comma delimited parameter for the names,values and the last value must be the total(e.g.: Quarter,25,Dime,10,Nickel,5,Penny,1,100) so I can process your request");
                        Console.Write("Input: ");

                       string anarchyCurency = Console.ReadLine();

                       if (anarchyCurency.Length <= 0)
                       {
                           Console.ForegroundColor = ConsoleColor.Red;
                           Console.WriteLine("The input value is invalid");
                           Console.ResetColor();
                           break;
                       }

                       Console.WriteLine();

                       try
                       {                           
                           GetCurrencyValues(anarchyCurency);
                       }
                       catch (Exception)
                       {
                           //TODO: Manage the exception
                           Console.ForegroundColor = ConsoleColor.Red;
                           Console.WriteLine("Ooops, something went wrong! Sorry for the inconvenience!");
                           Console.ResetColor();
                       }
                        break;
                    #endregion
                    #region [ Option 3]
                    case "3":
                        Console.ForegroundColor = ConsoleColor.DarkGreen;
                        Console.WriteLine("Byeeeee");
                        Console.ReadLine();

                        return;
                    #endregion
                    default:
                        break;
                }               
            }           

        }
        #endregion

        #region [ Methods ]
        /// <summary>
        /// Outputs the number of ways pennies, nickels, dimes and quarters can be combined to sum exactly $1
        /// 
        /// These solution assume that we always will have the following variables and values pennies(1) , nickels (5), dimes (10) and quarters (25) 
        /// 
        /// Based on that, the algorithm will try to find all the possible combinations to get the <para>total</para>
        /// </summary>
        /// <param name="total">Sum expected</param>
        /// <example>
        /// <code> Combinations(100);</code>
        /// Quarter Dimes   Nickels Penn
        /// 0       0       0       100
        /// 0       0       1       95
        /// 0       0       2       90
        /// 0       0       3       85
        /// 0       0       4       80
        /// 0       0       5       75
        /// ...
        /// ...
        /// ...
        /// 3       1       0       15
        /// 3       1       1       10
        /// 3       1       2       5
        /// 3       1       3       0
        /// 3       2       0       5
        /// 3       2       1       0
        /// 4       0       0       0
        /// 
        /// Total Combinations: 242
        /// </example>
        /// <exception cref="ArgumentException">if <para>total</para> is less than 0</exception>
        static void CurrencyUnderDemocracy(int total)
        {
            if (total < 0)
                throw new ArgumentException("The total parameter must be 0 or greater than 0");

            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("Quarter\tDimes\tNickels\tPennies");
            Console.ResetColor();

            int quarter = 25;
            int dime = 10;
            int nickel = 5;

            int count = 0;

            //Get the number of quarters I need for total
            for (int i = 0; i <= total / quarter; i++)
            {
                //Once I chose quarters, how mmuch money I have for dimes, nickels and pennies
                int dimes = total - (quarter * i);

                //Get the number of dimes I need for current total (dimes)
                for (int j = 0; j <= dimes / dime; j++)
                {
                    //Once I choose dimes, it's time to verify how much money I have to spare as nickels and pennies
                    int nickels = dimes - (dime * j);

                    //Get the number of nickels I can use for my currrent total (nickels)
                    for (int k = 0; k <= nickels / nickel; k++)
                    {
                        //The remainder is the total pennies I could use
                        int pennies = nickels - (nickel * k);

                        Console.WriteLine("{0}\t{1}\t{2}\t{3}", i, j, k, pennies);

                        count++;
                    }
                }

            }

            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("Total combinations: {0}", count);
            Console.ResetColor();

        }

        static void GetCurrencyValues(string input)
        {
            //check is not empty string
            if (input.Length <= 0)
                throw new ArgumentException("The input string cannot be empty or null");

            //split my input to get names and values
            string[] currency = input.Split(new char[] { ',' });

            int length = (currency.Length) / 2;                             

            string[] names = new string[length];
            double[] values = new double[length];
            //the last values is my base to calculate the other currency values
            double total = int.Parse(currency[currency.Length - 1]);

            int j = 0;
            for (int i = 0; i < currency.Length - 1; i += 2)
            {
                names[j] = currency[i];

                double result = 0;
                if (double.TryParse(currency[i + 1], out result))
                    values[j] = total / result;
                else
                    throw new ArgumentException(string.Format("The value for {0} is invalid",names[j]));

                    j++;
            }

            //print header names
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            for (int i = 0; i < names.Length; i++)
            {
                Console.Write("{0}\t", names[i]);
            }
            Console.ResetColor();
            Console.WriteLine();

            int count = CurrencyUnderAnarchy(values, new double[values.Length], 0, total);

            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("Total combinations: {0}", count);
            Console.ResetColor();

        }

        /// <summary>
        /// Since the United States Federal Reserve collapsed numerous millatiy dictatorships take over control the country and each one install their own currency.  
        /// 
        /// This method allow us to know the combinations to get a sum of an arbitrary currency
        /// </summary>
        /// <param name="input">Currency values. Must be listed from higher to lower</param>
        /// <param name="count">Store the Currency values counters. This always must be an empty array of the same length of <para>input</para></param>
        /// <param name="index">Current index in the iteration. This always must be 0 when invoke the method.</param>
        /// <param name="total">Sum expected </param>
        /// <returns>The total number of combinations</returns>
        /// <exception cref="ArgumentException">if <para>total</para> is less than 0 or <para>input</para> or <para>count</para> is null </exception>
        static int CurrencyUnderAnarchy(double[] input, double[] count, int index, double total)
        {
            if (input == null || count == null)
                throw new ArgumentException("Neither one input or count arrays could be null");
            if (total < 0)
                throw new ArgumentException("The total parameter must be 0 or greater than 0");

            int totalCount = 0;

            //if it is the last position or the lesser value of the currencies
            if (index == input.Length - 1)
            {
                //The last remainder 
                count[index] = total;

                //Print the counters
                string temp = string.Empty;
                for (int j = 0; j < count.Length; j++)
                {
                    temp += string.Format("{0}\t", count[j]);

                    totalCount++;
                }

                Console.WriteLine(temp);

                return totalCount /count.Length;
            }

            //Get the number of  values assigned in input[index] I need for total
            for (int i = 0; i <= total / input[index]; i++)
            {
                //Once selected, how much I have to spare with the other values
                double current = total - (input[index] * i);
                //Store the number of combinations for this value until now
                count[index] = i;

                //Recursive call with the actual values and return the total combinations until this point
                totalCount += CurrencyUnderAnarchy(input, count, index + 1, current);
            }

            return totalCount;
        }
        #endregion 
    }
}
